package com.coffee.core.util;

import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 将JavaBean对象转为Map对象
 *
 * @author Xulg
 * Created in 2019-07-10 16:22
 */
public class BeanConvert2MapUtils {

    private static final ConcurrentHashMap<Class<?>, List<PropertyDescriptor>> PROPERTY_DESCRIPTOR_CACHE = new ConcurrentHashMap<>();

    /**
     * JavaBean转换为Map<String,String>
     *
     * @param bean                th target bean
     * @param ignorePropertyNames 忽略属性名称
     * @return the map data
     */
    public static Map<String, String> convert2Map(Object bean, String... ignorePropertyNames) {
        if (bean == null) {
            return null;
        }
        Class<?> beanClass = bean.getClass();
        if (PROPERTY_DESCRIPTOR_CACHE.get(beanClass) == null) {
            synchronized (BeanConvert2MapUtils.class) {
                PropertyDescriptor[] candidatePropertyDescriptors = BeanUtils.getPropertyDescriptors(beanClass);
                CopyOnWriteArrayList<PropertyDescriptor> list = new CopyOnWriteArrayList<>();
                for (PropertyDescriptor descriptor : candidatePropertyDescriptors) {
                    if (!"class".equalsIgnoreCase(descriptor.getName())) {
                        list.add(descriptor);
                    }
                }
                PROPERTY_DESCRIPTOR_CACHE.put(beanClass, list);
            }
        }
        List<PropertyDescriptor> propertyDescriptors = PROPERTY_DESCRIPTOR_CACHE.get(beanClass);
        Map<String, String> data = new HashMap<>(propertyDescriptors.size());
        List<String> ignorePropertyNameList = (ignorePropertyNames == null)
                ? new ArrayList<>(0) : Arrays.asList(ignorePropertyNames);
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String propertyName = propertyDescriptor.getName();
            Object propertyValue = readPropertyValue(propertyDescriptor, bean);
            // 忽略空值
            if (propertyName == null || propertyValue == null
                    || ignorePropertyNameList.contains(propertyName)) {
                continue;
            }
            data.put(propertyName, propertyValue.toString());
        }
        return data;
    }

    private static Object readPropertyValue(PropertyDescriptor descriptor, Object bean) {
        try {
            return descriptor.getReadMethod().invoke(bean);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
